﻿// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

using Admin.NET.Application.Entity;
using Admin.NET.Application.Inventory.InventoryService;
using Admin.NET.Application.Service.OrderInfo;
using Admin.NET.Application.Service.Warehouse;
using Admin.NET.Application.Service.WcsService;
using Admin.NET.Application.Service.WcsTask;
using AngleSharp.Dom;
using Aop.Api.Domain;
using Elastic.Clients.Elasticsearch;
using MyDemo.Application.Service.Plc;
using NewLife.Remoting;
using Newtonsoft.Json;
using SixLabors.ImageSharp.ColorSpaces;
using System.Data;
using WCS.Application.Entity.Warehous;
using WCS.Application.Service.Inventory.DTO;
using WCS.Application.Service.Rcs;
using static MyDemo.Application.Service.Plc.MqttService;


namespace WCS.Application.Service.Inventory;


public class InventorySFPService : IDynamicApiController, ITransient
{
    #region 服务注入
    private readonly SqlSugarRepository<WarehousePositionEntity> _warehouseRep;
    private readonly SqlSugarRepository<InventoryManagementEntity> _inventoryRep;
    //动作表
    private readonly SqlSugarRepository<AgvactionsEntity> _agvactionsRep;

    // 注入库存服务
    private readonly InventoryService _inventoryService;
    //注入Redis获取
    //private readonly MqttClientService _mqttClientService;
    //注入任务服务
    private readonly WcsTaskService _taskService;
    private readonly OrderInfoService _orderInservice;
    private readonly DispatchService _dispatchService;

    //private readonly ILogger _llog;
    private readonly UserManager _userinfo;
    private readonly MqttService _sysMyService;
    private readonly SysCacheService _sysCacheService;



    List<SFPInventoryDto> list = new List<SFPInventoryDto>();
    int i = 0;
    //仓库code
    private string WarehosuCode;
    //库位编码
    private string WarehousePositionCode;
    #endregion

    private readonly SqlSugarRepository<WCSTaskManagementEntity> _wcsTaskEntity;

    public InventorySFPService(
        SqlSugarRepository<WarehousePositionEntity> warehouseRep,
        SqlSugarRepository<InventoryManagementEntity> inventoryRep,

        InventoryService inventoryService,
        WarehouseInfomationService warehouseInfomationService,
        WcsTaskService taskService,
        OrderInfoService orderInfoService,
        UserManager userinfo,
        DispatchService dispatchService,
        SqlSugarRepository<WCSTaskManagementEntity> wcsTaskEntity,
        MqttService myService, SysCacheService sysCacheService,
        SqlSugarRepository<AgvactionsEntity> agvactionsRep
        )
    {
        _warehouseRep = warehouseRep;
        _inventoryRep = inventoryRep;

        _inventoryService = inventoryService;
        _taskService = taskService;  //_llog = llog;
        _orderInservice = orderInfoService;
        _dispatchService = dispatchService;

        _userinfo = userinfo;
        _wcsTaskEntity = wcsTaskEntity;
        _sysMyService = myService;
        _sysCacheService = sysCacheService;
        _agvactionsRep = agvactionsRep;

    }

    #region 暂存入库

    /// <summary>  
    /// 调用 TemporaryStorage 方法并在成功后执行后续操作  
    /// </summary>  
    /// <param name="sfpInventoryDto">SFP库存数据传输对象</param>  
    /// <returns>任务执行结果</returns>  
    [UnitOfWork]
    public async Task<bool> TemporaryInbound(TemporarilyWarehouse sfpInventoryDto)
    {
        SerilogHelper.WriteLog("TemporaryInbound", "start");
        try
        {
            SerilogHelper.WriteLog("TemporaryInbound", "sfpInventoryDto", JsonHelper.ObjectToJson(sfpInventoryDto));
            if (string.IsNullOrEmpty(sfpInventoryDto.Hoistopening))
                throw Oops.Oh("入库口不能为空", nameof(sfpInventoryDto.Hoistopening));
            if (string.IsNullOrEmpty(sfpInventoryDto.Palcode))
                throw Oops.Oh("托盘编码不能为空", nameof(sfpInventoryDto.Palcode));


            var allDevices = App.GetConfig<List<DerivesInfo>>("MQTT:Devices");

            // 起始点位为提升机入库，正常为12,22,32,42,52
            var startPoint = sfpInventoryDto.Hoistopening;
            var startDevice = allDevices.Where(d => d.DeviceType == 1).FirstOrDefault(d => d.DeviceGroup.Contains(sfpInventoryDto.Hoistopening));
            if (startDevice is null)
            {
                SerilogHelper.WriteLog("TemporaryInbound", $"提升机未找到：{sfpInventoryDto.Hoistopening}");

                return false;
            }
            // 目标点位,格式为 "Y,X"
            var endPoint = "";
            // 目标库位对应的通道点位
            var channelPoint = "";
            // 目标层
            int storey = 1;

            #region 查找空闲可用的RGV，推荐到当前RGV所在的层
            var allRGV = new List<DerivesInfo>();
            foreach (var derive in allDevices)
            {
                if (derive.DeviceType == 0)
                {
                    var rgv = _sysCacheService.Get<DerivesInfo>(derive.DeviceId);
                    if (rgv is not null)
                    {
                        rgv.DeviceEnable = derive.DeviceEnable;
                        rgv.Storey = derive.Storey;
                        if (rgv.DeviceEnable && rgv.DeviceStatus == 0)
                            allRGV.Add(rgv);
                        //derive.DeviceStatus = rgv.DeviceStatus;
                        //derive.LocationPoint = rgv.LocationPoint;
                    }
                }
            }
            #endregion

            if (allRGV.Count == 0)
            {
                SerilogHelper.WriteLog("TemporaryInbound", $"提升机未找到空闲四向车：{sfpInventoryDto.Hoistopening}");
                return false;
            }
            allRGV = allRGV.OrderBy(d => d.DeviceId).ToList();
            SerilogHelper.WriteLog("TemporaryInbound", "可用小车：", JsonHelper.ObjectToJson(allRGV));

            // 获取所有启用的库位
            Expression<Func<WarehousePositionEntity, bool>> locExp = whereExp =>
                whereExp.Isenable == WStatusEnum.Enable
                ;
            var locList = await _warehouseRep.Context.CopyNew()
                .Queryable<WarehousePositionEntity>()
                .Where(locExp)
                .ToListAsync();

            // 获取当前库存
            var stockList = await _inventoryRep.Context.CopyNew().Queryable<InventoryManagementEntity>().ToListAsync();
            var stockAll = stockList.ToDictionary(it => it.WarehousCode + "|" + it.WarehousePositionCode, it => it);

            //查询数据库动作表
            var carActionsList = await _agvactionsRep.Context.CopyNew()
                .Queryable<AgvactionsEntity>()
                .Where(c => c.Status == ActionStatusEnum.Pending)
                .ToListAsync();

            // 遍历四向车
            var inboundSuccess = false;
            foreach (var rgv in allRGV)
            {
                SerilogHelper.WriteLog("TemporaryInbound", "当前小车：", rgv.DeviceId);
                // 判断小车是否有其他在执行任务
                if (carActionsList.Exists(r => r.DeviceId == rgv.DeviceId))
                {
                    SerilogHelper.WriteLog("TemporaryInbound", $"当前小车有其他任务在执行: {rgv.DeviceId}");
                    continue;
                }

                #region 查找库位

                //if (startDevice.DeviceId == "41")
                //{
                //    locExp = whereExp =>
                //        whereExp.Isenable == WStatusEnum.Enable
                //        && whereExp.Storey == 1
                //    ;
                //}

                // 查找当前小车所在层所有可用库位
                var locAll = locList
                    .Where(it => it.Storey == rgv.Storey)
                    .ToDictionary(it => it.WarehousCode + "|" + it.Code, it => it);
                //var locAll = await _warehouseRep.AsQueryable().Where(locExp).ToDictionaryAsync(it => it.WarehousCode + "|" + it.Code, it => it);


                // 获取每个可用库位，到通道口最近
                var dicEnableLoc = new Dictionary<string, double>();
                foreach (var item in locAll)
                {
                    // 暂存入库找空库位，没有库存
                    if (!stockAll.ContainsKey(item.Key))
                    {
                        var loc = item.Value;
                        // 获取每个双深位第一个入口的点位
                        var point = loc.Poiint.Split('|')[0];
                        double length = GetDistanceData(startDevice?.Point, point);
                        if (!dicEnableLoc.ContainsKey(item.Key))
                            dicEnableLoc.Add(item.Key, length);
                        else
                            dicEnableLoc[item.Key] = length;

                        //// 如果存在空闲RGV，则RGV当前层所在的库位优先级提升
                        //if (allRGV.Exists(rgv => rgv.DeviceStatus == 0 && rgv.Storey == loc.Storey))
                        //    dicEnableLoc[item.Key] = length / 100;

                    }
                }
                if (dicEnableLoc.Count == 0)
                {
                    SerilogHelper.WriteLog("TemporaryInbound", "当前层没有找到空库位:", rgv.Storey.ToString());

                }
                SerilogHelper.WriteLog("TemporaryInbound", "dicEnableLoc", JsonHelper.ObjectToJson(dicEnableLoc));

                // 找到距离最近的库位
                var optimalPosition = dicEnableLoc
                    .OrderBy(x => x.Value).ThenBy(x => x.Key)
                    .FirstOrDefault();
                var endLoc = "";
                if (optimalPosition.Key != null)
                {
                    endLoc = optimalPosition.Key;
                    endPoint = locAll[endLoc].Poiint.Split('|')[0];
                    channelPoint = locAll[endLoc].ChannelLocation;
                    storey = locAll[endLoc].Storey;
                    SerilogHelper.WriteLog("TemporaryInbound", $"最优库位: {endLoc}, 距离: {optimalPosition.Value}");
                }
                else
                {
                    SerilogHelper.WriteLog("TemporaryInbound", $"当前小车未找到可用库位: {rgv.DeviceId}");
                    continue;
                }
                #endregion


                #region 插入库存，占用货格

                Expression<Func<InventoryManagementEntity, bool>> whereExpression = whereExp =>
                            whereExp.Palcode.Equals(sfpInventoryDto.Palcode)
                            ;
                var data = await _inventoryRep.Context.CopyNew().Queryable<InventoryManagementEntity>().Where(whereExpression).FirstAsync();
                if (data is null)
                {
                    var entity = new InventoryManagementEntity()
                    {
                        WarehousCode = locAll[endLoc].WarehousCode,
                        WarehousePositionCode = locAll[endLoc].Code,
                        Palcode = sfpInventoryDto.Palcode,
                        MaterialCode = "",
                        MaterialName = "",
                        Batch = "",
                        BoxCode = "",
                        DateofReceipt = DateTime.Now,
                        Status = InventoryStatus.冻结, // 反馈后再改状态  
                                                     //Available = 1,
                        Number = 1,
                        CreateTime = DateTime.Now,
                        CreateUserId = _userinfo.UserId,
                        CreateUserName = _userinfo.RealName

                    };
                    var rtInsert = await _inventoryRep.Context.CopyNew().Insertable(entity).ExecuteCommandAsync();
                    SerilogHelper.WriteLog($"添加库存结束: {rtInsert}");
                }
                else
                {
                    data.WarehousePositionCode = locAll[endLoc].Code;
                    var rtUpdate = await _inventoryRep.Context.CopyNew().Updateable(data).ExecuteCommandAsync();
                    SerilogHelper.WriteLog($"添加库存结束: {rtUpdate}");
                }

                #endregion

                #region 添加WCS任务

                // 写入wcs任务表
                var wcsTask = new WCSTaskManagementEntity()
                {
                    TaskNumber = DateTime.Now.ToString("INyyyyMMddHHmmssfff_") + sfpInventoryDto.TaskNo.ToString(),
                    Type = TaskTypeEnum.Inbound,
                    Status = TaskStatusEnmu.Underay,
                    OrderId = sfpInventoryDto.TaskNo.ToString(),
                    Loc = endLoc,
                    HoistOpening = sfpInventoryDto.Hoistopening,
                    TrayCode = sfpInventoryDto.Palcode,
                };
                var rtInsertTask = await _wcsTaskEntity.Context.CopyNew().Insertable(wcsTask).ExecuteCommandAsync();
                SerilogHelper.WriteLog("TemporaryInbound", $"入库任务下发完成同步库存表");

                #endregion

                #region 下发给RCS任务

                var requsetBody = new BaseRequest()
                {
                    TaskId = sfpInventoryDto.TaskNo.ToString(),
                    AgvCode = rgv.DeviceId,
                    AgvCurrentPos = rgv.LocationPoint,
                    Startcoord = startPoint,
                    StoragePosCoord = endPoint,
                    TargetPointCoord = channelPoint,
                    TargetFloor = storey,
                    Palletcode = sfpInventoryDto.Palcode,
                    Agvtype = "inbound",
                    Speed = 1
                };
                SerilogHelper.WriteLog("TemporaryInbound", $"{sfpInventoryDto.Hoistopening}入库下发给RCS任务-requsetBody,{rgv.DeviceId}", JsonHelper.ObjectToJson(requsetBody));
                
                try
                {
                    var rtProcess = await _dispatchService.ProcessRequest(requsetBody);
                    SerilogHelper.WriteLog("TemporaryOutbound", $"{sfpInventoryDto.Hoistopening}出库下发给RCS任务-rtProcess,{rgv.DeviceId}", JsonHelper.ObjectToJson(rtProcess));

                    if (rtProcess.IsSuccess == false)
                    {
                        SerilogHelper.WriteLog("TemporaryInbound", $"{rgv.DeviceId}入库分配路径失败", JsonHelper.ObjectToJson(rtProcess));
                        continue;
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.WriteErrorLog("TemporaryOutbound", ex);
                    continue;
                }
                
                #endregion



                inboundSuccess = true;
                break;
            }

            if (!inboundSuccess)
                SerilogHelper.WriteLog("TemporaryInbound", $"{sfpInventoryDto.Hoistopening}未成功分配任务");


            #region 添加WCS任务

            #endregion

            // 返回入库结果
            return inboundSuccess;
        }
        catch (Exception ex)
        {
            LogHelper.WriteErrorLog("TemporaryInbound", ex);
            return false;
        }
        finally
        {
            SerilogHelper.WriteLog("TemporaryInbound", "end");
        }

        //return $"任务 {tasknumber} 已成功执行并触发后续操作";
    }

    #endregion



    #region 暂存出库

    /// <summary>  
    /// 暂存出库  
    /// </summary>  
    /// <param name="sfpInventoryDto">SFP库存数据传输对象</param>  
    /// <returns>任务执行结果</returns>  
    public async Task<bool> TemporaryOutbound(TemporarilyOutbound sfpInventoryDto)
    {
        SerilogHelper.WriteLog("TemporaryOutbound", "sfpInventoryDto", JsonHelper.ObjectToJson(sfpInventoryDto));
        try
        {

            if (string.IsNullOrEmpty(sfpInventoryDto.Hoistopening))
            {
                SerilogHelper.WriteLog("TemporaryOutbound", $"出库口为空");
                return false;
            }
            if (string.IsNullOrEmpty(sfpInventoryDto.Palcode))
            {
                SerilogHelper.WriteLog("TemporaryOutbound", $"托盘编码为空");
                return false;
            }

            // 获取所有设备
            var allDevices = App.GetConfig<List<DerivesInfo>>("MQTT:Devices");

            // 目标点位为提升机出库库，11,21,31,41,51
            var endDeviceGroup = allDevices.
                Where(d => d.DeviceType == 1 && d.DeviceEnable == true)
                .FirstOrDefault(d => d.DeviceGroup.Contains(sfpInventoryDto.Hoistopening.Substring(3) + "1"));
            var endPoint = endDeviceGroup.OutboudEndDevice;


            // 起始点位,格式为 "Y,X"
            // 获取当前库存
            var palStock = await _inventoryRep.Context.CopyNew()
                .Queryable<InventoryManagementEntity>()
                .Where(it => it.Palcode == sfpInventoryDto.Palcode && it.Status == 0)
                .FirstAsync();
            if (palStock is null)
            {
                SerilogHelper.WriteLog("TemporaryOutbound", $"未查到托盘库存：{sfpInventoryDto.Palcode}");
                return false;
            }

            // 获取库位点位信息
            Expression<Func<WarehousePositionEntity, bool>> locExp = it =>
                it.Isenable == WStatusEnum.Enable
                && it.Code == palStock.WarehousePositionCode
                ;
            var loc = await _warehouseRep.Context.CopyNew()
                .Queryable<WarehousePositionEntity>()
                .Where(locExp)
                .FirstAsync();
            if (loc is null)
            {
                SerilogHelper.WriteLog("TemporaryOutbound", $"未查到库位信息：{palStock.WarehousePositionCode}");
                return false;
            }

            var startPoint = loc.Poiint.Split('|')[0];

            // 起始库位对应的通道点位
            var channelPoint = loc.ChannelLocation;
            // 目标层
            int storey = loc.Storey;

            #region 查找库存所在层可用的RGV

            var allRGV = new List<DerivesInfo>();
            foreach (var derive in allDevices)
            {
                if (derive.DeviceType == 0)
                {
                    var rgv = _sysCacheService.Get<DerivesInfo>(derive.DeviceId);
                    if (rgv is not null)
                    {
                        rgv.DeviceEnable = derive.DeviceEnable;
                        rgv.Storey = derive.Storey;
                        if (rgv.Storey == storey & rgv.DeviceEnable && rgv.DeviceStatus == 0)
                            allRGV.Add(rgv);
                    }
                }
            }
            if (allRGV.Count == 0)
            {
                SerilogHelper.WriteLog("TemporaryOutbound", $"未找到空闲四向车");
                return false;
            }

            #endregion


            #region 更新库存为冻结状态
            // 更新库存
            //_inventoryRep.AsUpdateable().SetColumns

            //SerilogHelper.WriteLog("TemporaryOutbound", "inster", JsonHelper.ObjectToJson(inster));

            #endregion

            #region 下发给RCS任务

            Random random = new Random();
            var isSuccess = false;

            //查询数据库动作表
            var carActionsList = await _agvactionsRep.Context.CopyNew()
                .Queryable<AgvactionsEntity>()
                .Where(c => c.Status == ActionStatusEnum.Pending)
                .ToListAsync();

            foreach (var rgv in allRGV)
            {
                SerilogHelper.WriteLog("TemporaryOutbound", "当前小车：", rgv.DeviceId);
                // 判断小车是否有其他在执行任务
                if (carActionsList.Exists(r => r.DeviceId == rgv.DeviceId))
                {
                    SerilogHelper.WriteLog("TemporaryOutbound", $"当前小车有其他任务在执行: {rgv.DeviceId}");
                    continue;
                }

                var requsetBody = new BaseRequest()
                {
                    TaskId = sfpInventoryDto.TaskNo,
                    AgvCode = rgv.DeviceId,
                    AgvCurrentPos = rgv.LocationPoint,
                    Startcoord = endPoint,
                    StoragePosCoord = startPoint,
                    TargetPointCoord = channelPoint,
                    TargetFloor = storey,
                    Palletcode = sfpInventoryDto.Palcode,
                    Agvtype = "outbound",
                    Speed = 1
                };
                SerilogHelper.WriteLog("TemporaryOutbound", $"{sfpInventoryDto.Hoistopening}出库下发给RCS任务-requsetBody,{rgv.DeviceId}", JsonHelper.ObjectToJson(requsetBody));
                try
                {
                    var rtProcess = await _dispatchService.ProcessRequest(requsetBody);
                    SerilogHelper.WriteLog("TemporaryOutbound", $"{sfpInventoryDto.Hoistopening}出库下发给RCS任务-rtProcess,{rgv.DeviceId}", JsonHelper.ObjectToJson(rtProcess));

                    if (rtProcess.IsSuccess == false)
                    {
                        SerilogHelper.WriteLog("TemporaryOutbound", $"{rgv.DeviceId}出库库分配路径失败", JsonHelper.ObjectToJson(rtProcess));
                        continue;
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.WriteErrorLog("TemporaryOutbound", ex);
                    continue;
                }
                isSuccess = true;
                break;
            }
            #endregion


            if (!isSuccess)
                SerilogHelper.WriteLog("TemporaryOutbound", $"{sfpInventoryDto.Hoistopening}未成功分配出库任务");

            return isSuccess;
        }
        catch (Exception ex)
        {
            LogHelper.WriteErrorLog("TemporaryOutbound_Error", ex);
            return false;
        }
        finally
        {
            SerilogHelper.WriteLog("TemporaryOutbound", "end");
        }
    }



    #endregion


    ///// <summary>
    ///// 暂存入库方法
    ///// </summary>
    ///// <param name="sfpInventoryDto"></param>
    ///// <param name="operationType"></param>
    ///// <returns></returns>
    ///// <exception cref="ArgumentNullException"></exception>
    //public async Task<TemporarilyWarehouseOutDTO> TemporaryStorage(TemporarilyWarehouse sfpInventoryDto)
    //{
    //    // 检查参数有效性  
    //    var start = "";//库口   
    //    var end = ""; // 终点坐标，格式为 "Y,X"


    //    //起点库口or提升机
    //    start = sfpInventoryDto.Hoistopening;

    //    SeedInventoryItems();
    //    if (sfpInventoryDto == null) throw new ArgumentNullException(nameof(sfpInventoryDto));
    //    if (string.IsNullOrEmpty(sfpInventoryDto.Palcode)) throw Oops.Oh("托盘编码不能为空", nameof(sfpInventoryDto.Palcode));
    //    //decimal remainingQuantity = sfpInventoryDto.Quantity;

    //    var Traylist = await _inventoryService.GetInventoryList(new InventoryManagementDto
    //    {
    //        //查询托盘码是否已用
    //        //MaterialCode = sfpInventoryDto.Materialcode,
    //        Palcode = sfpInventoryDto.Palcode,
    //        Status = InventoryStatus.正常
    //    });
    //    if (Traylist.Count > 0)
    //    {
    //        throw Oops.Oh($"{sfpInventoryDto.Palcode}托盘编码不能为空重复录入");
    //    }


    //    #region AGV信息
    //    var clientId = App.GetConfig<string>("MQTT:ClientId");

    //    // 查询缓存里面的agv信息
    //    var derivesInfo = App.GetConfig<List<DerivesInfo>>("MQTT:Devices");// _sysCacheService.Get<List<DerivesInfo>>("clientId");要从redis获取的
    //    #region 过滤AGV数据能否正常使用

    //    #endregion

    //    foreach (var item in derivesInfo)
    //    {
    //        if (item.DeviceType == 0)
    //        {
    //            if (item.DeviceStatus == 1)
    //            {

    //                throw Oops.Oh("没有可用的AGV都有任务");
    //            }
    //            else
    //            {
    //                if (item.DeviceEnable == 1)
    //                {
    //                    throw Oops.Oh("设备状态不正常");

    //                }

    //            }
    //        }
    //    }
    //    var idleagv = derivesInfo.Where(it => it.DeviceType == 0 && it.DeviceStatus == 0 && it.DeviceEnable == 0).ToList().FirstOrDefault();


    //    //根据agvid表示找出当前对应设备信息  这里应该是redis可用的和配置的比较
    //    var devices = App.GetConfig<List<DerivesInfo>>("MQTT:Devices");
    //    var startDevice = devices.FirstOrDefault(it => it.DeviceType == 0 && it.DeviceId.Equals(idleagv.DeviceId));

    //    #endregion
    //    SerilogHelper.WriteLog("TemporaryStorage", "查询到空闲AGV信息", JsonHelper.ObjectToJson(startDevice));


    //    #region 字典过滤最短距离
    //    //所有的库位表 or 空闲AGV楼层
    //    var Positlist = await _warehouseInfomationService.GetWarehousePositlist(new Admin.NET.Application.Service.Position.Dto.PositionPositionInput()
    //    {
    //        //Available = WStatusEnum.Disable,
    //        Isenable = WStatusEnum.Enable,
    //        Storey = startDevice.Storey//查询agv所在楼层

    //    });

    //    // 定义一个字典来存储库位和对应的距离  【分配库位逻辑】keys存放目标点信息
    //    string[] keys = new string[5];
    //    Dictionary<string, double> positionDistanceMap = new Dictionary<string, double>();
    //    var inventoryList = await GetFIFOInventoryAsync(new TemporarilyWarehouse { });

    //    InventoryManagementEntity inventory1 = inventoryList.DefaultIfEmpty().FirstOrDefault();
    //    if (Positlist.Count > 0)
    //    {
    //        foreach (WarehousePositionEntity item in Positlist)
    //        {
    //            double count = GetDistanceData(start, item.Poiint);
    //            var key = item.Storey.ToString() + "|" + item.Code + "|" + item.Poiint;
    //            positionDistanceMap[key] = count; // 将库位点和距离存储到字典中  
    //        }

    //        foreach (InventoryManagementEntity item in inventoryList)
    //        {
    //            // 过滤掉字典中 key[0] 等于          库存： 缺少库位的唯一标识编码 code还是要点位
    //            var keysToRemove = positionDistanceMap.Keys.Where(key => key.Split('|')[1] == item.WarehousePositionCode).ToList();
    //            foreach (var key in keysToRemove)
    //            {
    //                positionDistanceMap.Remove(key);
    //            }
    //        }

    //        // 找到距离最小的库位    
    //        var optimalPosition = positionDistanceMap.OrderBy(x => x.Value).FirstOrDefault();
    //        if (optimalPosition.Key != null)
    //        {
    //            keys = optimalPosition.Key.Split('|');
    //            end = keys[2];
    //            SerilogHelper.WriteLog($"最优库位: {keys[1]}, 距离: {optimalPosition.Value}");
    //        }
    //        #endregion

    //    }
    //    else
    //    {
    //        throw Oops.Oh("没有可用的库位");
    //    }

    //    //获取 目标点最优库位信息
    //    var warepotion = Positlist.Where(c => c.Code == keys[2].ToString());
    //    var entity = warepotion.FirstOrDefault();
    //    //任务id
    //    string tasknumber = "";
    //    //数据入库任务表占库位
    //    var sortid = await _taskService.GetSortIdWcs();
    //    tasknumber = await InsertWcsTask(new AddWcsTaskInput
    //    {
    //        TrayCode = sfpInventoryDto.Palcode,
    //        MaterialCode = sfpInventoryDto.Materialcode,
    //        Description = "通过PDA扫描入库",//枚举 通过PAD同步
    //        Type = TaskTypeEnum.Inbound,
    //        Status = TaskStatusEnmu.NoStarte,
    //        Isenable = WStatusEnum.Enable,
    //        HoistOpening = start,
    //        SortId = sortid,

    //    });
    //    //var c = true;

    //    var rtData = new TemporarilyWarehouseOutDTO()
    //    {
    //        TaskNumber = tasknumber,
    //        LocationPoint = startDevice.LocationPoint,
    //        StartPoint = start,
    //        EndPoint = end,
    //        EndLevel = startDevice.Storey,
    //        ChannelPoint = entity.ChannelLocation,
    //        DeviceId = startDevice.DeviceId,
    //        Palcode = sfpInventoryDto.Palcode,
    //    };
    //    return rtData;
    //    // 返回  开始 点结束， AGV标识，agv当前位置 任务号 , 层数，托盘码，当前库位点，类型
    //    //return (start, end, startDevice.DeviceId, startDevice.LocationPoint, tasknumber, startDevice.Storey, sfpInventoryDto.Palcode, entity.ChannelLocation, "inbound");

    //}




    ///// <summary>
    ///// 出库查询最近库位下发任务到AGV
    ///// </summary>
    ///// <param name="outhous"></param>
    ///// <param name="operationType"></param>
    ///// <returns></returns>
    ///// <exception cref="ArgumentNullException"></exception>
    //public async Task<(string, string, string, string, string, int, string, string, string)> TemporaryStorageExport(TemporarilyWarehouse outhous, string operationType)
    //{
    //    var start = outhous.Hoistopening;
    //    var end = ""; // 终点坐标，货物库位 格式为 "y,x"

    //    if (outhous == null) throw new ArgumentNullException(nameof(TemporarilyWarehouse));
    //    if (string.IsNullOrEmpty(outhous.Hoistopening)) throw Oops.Oh("出库口提升机不能为空", nameof(operationType));

    //    //AGV所有信息
    //    #region AGV信息
    //    var clientId = App.GetConfig<string>("MQTT:ClientId");

    //    // 查询缓存里面的agv信息
    //    var derivesInfo = _sysCacheService.Get<DerivesInfo>(clientId);

    //    if (derivesInfo.DeviceType == 0)
    //    {
    //        if (derivesInfo.DeviceStatus == 1)
    //        {

    //            throw Oops.Oh("没有可用的AGV都有任务");
    //        }
    //        else
    //        {
    //            if (derivesInfo.DeviceEnable == 1)
    //            {
    //                throw Oops.Oh("AGV设备异常");

    //            }

    //        }
    //    }
    //    //根据agvid表示找出当前对应设备信息
    //    var devices = App.GetConfig<List<DerivesInfo>>("MQTT:Devices");
    //    var startDevice = devices.FirstOrDefault(it => it.DeviceType == 0 && it.DeviceId.Equals(derivesInfo.DeviceId));

    //    //四向车坐标能否获取楼层 另一种获取agv信息方法
    //    var LocationAvgt = await _sysMyService.RedisGet(clientId);

    //    var Agvmation  = JsonHelper.JsonToObject<DerivesInfo>(LocationAvgt);

    //    #endregion

    //    //每层都有同类物料 但托盘码不同啊      
    //    var inventories = await _inventoryService.GetInventoryList(new InventoryManagementDto()
    //    {
    //        //Available = WStatusEnum.Disable,
    //        // WarehousePositionCode = outhous.Materialcode,
    //        Palcode = outhous.Palcode,
    //        Status = InventoryStatus.正常,
    //        MaterialCode = outhous.Materialcode
    //    });
    //    if (!inventories.Any())
    //    {
    //        throw Oops.Oh("库存无此物料");
    //    }

    //    //过滤条件库位筛选与优先级排序
    //    inventories.Where(i => i.Available == WStatusEnum.Enable)
    //        .OrderBy(i => i.DateofReceipt)
    //        .ThenBy(i => i.CreateTime)
    //        .ToList().FirstOrDefault();
    //    InventoryManagementEntity inventory = inventories.DefaultIfEmpty().FirstOrDefault();

    //    //看是库位点位 还是库位编码 又必要查一次不    +agv当前层数
    //    var wareposiin = await _warehouseInfomationService.GetWarehousePositlist(new Admin.NET.Application.Service.Position.Dto.PositionPositionInput
    //    {
    //        Code = inventory.WarehousePositionCode,//唯一标识
    //        // 需要agv的层
    //        Storey = Agvmation.Storey
    //    });
    //    WarehousePositionEntity positionEntity = wareposiin.FirstOrDefault();
    //    SerilogHelper.WriteLog("查询到本地物料信息开始写任务");
    //    end = positionEntity.Poiint;
    //    //数据入库任务表占库位  SortI获取当前下发中任务数最大+1
    //    var sortid = await _taskService.GetSortIdWcs();

    //    var tasknumber = await InsertWcsTask(new AddWcsTaskInput
    //    {
    //        TrayCode = outhous.Palcode,
    //        MaterialCode = outhous.Materialcode,
    //        Description = "通过APD扫描出库",//枚举 通过PAD同步
    //        Type = TaskTypeEnum.Outbound,
    //        Status = TaskStatusEnmu.NoStarte,
    //        Isenable = WStatusEnum.Enable,
    //        HoistOpening = start,
    //        SortId = sortid,
    //        CountNumber = 0,
    //        CreateTime = DateTime.Now,
    //        CreateUserId = _userinfo.UserId,
    //        DownDate = DateTime.Now
    //    });
    //    //改本地库存表
    //    UpdateInventoryManagementDtoInput updateInventory = new UpdateInventoryManagementDtoInput
    //    {
    //        MaterialCode = inventory.MaterialCode,
    //        Available = WStatusEnum.Enable,
    //        Batch = "",
    //        BoxCode = "",
    //        MaterialName = inventory.MaterialName,
    //        Palcode = inventory.Palcode,
    //        Status = InventoryStatus.冻结,//等有反馈后再改状态
    //        WarehousePositionCode = positionEntity.Code,//inventory.WarehousePositionCode,
    //        Id = inventory.Id

    //    };

    //    await _inventoryService.UpdateInventory(updateInventory);
    //    SerilogHelper.WriteLog("TemporaryStorageExport", "发送任务至 AGV 调度系统",$"{tasknumber}出库任务下发开始");

    //    // 发送任务至 AGV 调度系统 //返回 开始 点，结束， AGV标识 任务号, 层数，agv实体，托盘码，当前库位点，类型
    //    return (start, end, Agvmation.DeviceId, Agvmation.LocationPoint, tasknumber, Agvmation.Storey, positionEntity.ChannelLocation, outhous.Palcode, "outbound");

    //    //优先选择距离库位最近的空闲 AGV
    //    //如果当前楼层无可用 AGV，则检查上层是否有可用 AGV 并允许跨层调度
    //    //任务生成与执行
    //    //将库位状态标记为 "待抽空" 并冻结库存数量
    //    //状态跟踪与库存更新


    //}

    ///// <summary>
    ///// 出库
    ///// </summary>
    ///// <param name="sfpInventoryDto"></param>
    ///// <param name="operationType"></param>
    ///// <returns></returns>
    //public async Task<string> TemporaryStorageExportInterface(TemporarilyWarehouse sfpInventoryDto, string operationType)
    //{
    //    // 调用 TemporaryStorageExport 方法  
    //    var (start, end, agv, LocationPoint, tasknumber, storey, traycode, position, type) = await TemporaryStorageExport(sfpInventoryDto, operationType);
    //    var Tsakid = tasknumber;

    //    // 如果 TemporaryStorage 成功，执行后续操作  
    //    _ = Task.Run(async () =>
    //    {

    //        var Tasklist = await _taskService.GetWcsTaskList(new WcsTaskDto
    //        {
    //            TaskNumber = Tsakid
    //            //  Status = (int)TaskStatusEnmu.Underay
    //        });
    //        WCSTaskManagementEntity WcsTask = Tasklist.DefaultIfEmpty().FirstOrDefault();

    //        var inventorylist = await _inventoryService.GetInventoryList(new InventoryManagementDto
    //        {
    //            Palcode = WcsTask.TrayCode,
    //            MaterialCode = WcsTask.MaterialCode

    //        });
    //        SerilogHelper.WriteLog("TemporaryStorageExportInterface", "出库更新操作开始","${Tsakid}出库任务下发完成-更新任务和库存开始");

    //        InventoryManagementEntity inventory = inventorylist.DefaultIfEmpty().FirstOrDefault();

    //        //改本地库存表
    //        UpdateInventoryManagementDtoInput updateInventory = new UpdateInventoryManagementDtoInput
    //        {
    //            MaterialCode = inventory.MaterialCode,
    //            Available = WStatusEnum.Enable,
    //            Batch = "",
    //            BoxCode = "",
    //            DateofReceipt = DateTime.Now,
    //            MaterialName = inventory.MaterialName,
    //            Palcode = inventory.Palcode,
    //            Status = InventoryStatus.出库完成,//等有反馈后再改状态
    //            WarehousePositionCode = inventory.WarehousePositionCode,
    //            UpdateTime = DateTime.Now,
    //            UpdateUserId = _userinfo.UserId,
    //            UpdateUserName = _userinfo.RealName,
    //            Id = inventory.Id

    //        };

    //        await _inventoryService.UpdateInventory(updateInventory);
    //        await UpdateTaskStatusAsync(Tsakid, 0, "");
    //        #region 下发给RCS任务
    //        var requsetBody = new BaseRequest()
    //        {
    //            TaskId = Tsakid,
    //            AgvCode = agv,
    //            AgvCurrentPos = LocationPoint,
    //            Startcoord = start, // 逻辑待补充
    //            TargetFloor = storey,
    //            TargetPointCoord = position,  // 逻辑待补充，通道点位
    //            StoragePosCoord = end,
    //            Palletcode = sfpInventoryDto.Palcode,
    //            Agvtype = type,
    //            Speed = 1
    //        };

    //        var rtProcess = await _dispatchService.ProcessRequest(requsetBody);
    //        //  逻辑待补充，加日志
    //        SerilogHelper.WriteLog("TemporaryStorageExportInterface", "出库下发给RCS任务", JsonHelper.ObjectToJson(requsetBody));
    //        #endregion
    //    });

    //    return $"任务 {Tsakid} 已成功执行并触发后续操作";
    //}



    /// <summary>
    /// RCS调用反馈，更新任务表状态和响应
    /// </summary>
    /// <param name="taskId"></param>
    /// <param name="status"></param>
    /// <param name="response"></param>
    /// <returns></returns>
    public async Task<bool> UpdateTaskStatusAsync(string taskId, int status, string response)
    {
        // 检查参数有效性  
        var Listing = await _taskService.GetWcsTaskList(new WcsTaskDto
        {
            TaskNumber = taskId.ToString(),
            //  Status = (int)TaskStatusEnmu.Underay
        });
        if (Listing == null || Listing.Count() == 0)
        {
            throw Oops.Oh($"任务号为 {taskId} 的任务不存在或状态不正确");
        }

        // 更新任务状态   
        var task = Listing.FirstOrDefault();

        var taskUpdate = new UpdateWcsTaskInput
        {
            TaskNumber = task.TaskNumber,
            DownDate = task.DownDate,
            FinishDate = DateTime.Now,
            Status = (TaskStatusEnmu)status, //任务走完Completed已完成
            ResPonse = response,
            Id = task.Id,
            SortId = 0,
            UpdateUserId = _userinfo.UserId,
            UpdateUserName = _userinfo.RealName,
            UpdateTime = DateTime.Now

        };

        if (await _taskService.UpdateWcsTask(taskUpdate) > 0)
        {
            return true;

        }
        else
        {
            throw Oops.Oh($"任务号为 {taskId} 的任务更新失败");
        }
    }



    /// <summary>
    /// 记录任务表
    /// </summary>
    /// <param name="Input"></param>
    /// <returns></returns>

    public async Task<string> InsertWcsTask(AddWcsTaskInput Input)
    {
        #region PDA过来的数据 找库位后记录

        #endregion
        var data = await _taskService.InsertWcsTask(Input);

        return data;
    }
    /// <summary>  
    /// 检查是否存在全部合格数据  
    /// </summary>  
    /// <param name="containerCode">容器编码</param>  
    /// <returns>是否存在全部合格数据</returns>  
    private async Task<bool> CheckAllQualifiedDataAsync(string containerCode)
    {
        // 模拟检查逻辑  
        await Task.Delay(100); // 模拟异步操作  
        return true; // 假设存在全部合格数据  
    }

    /// <summary>  
    /// 触发数据同步逻辑  
    /// </summary>  
    /// <param name="sfpInventoryDto">SFP库存数据传输对象</param>  
    private async Task TriggerDataSyncAsync(SFPInventoryDto sfpInventoryDto)
    {
        // 模拟数据同步逻辑  
        await Task.Delay(100); // 模拟异步操作  
        Console.WriteLine($"触发数据同步: {sfpInventoryDto.note_2}");
    }
    /// <summary>  
    /// 计算欧几里得距离  
    /// </summary>  
    /// <param name="start">起点坐标，格式为 "x,y"</param>  
    /// <param name="end">终点坐标，格式为 "x,y"</param>  
    /// <returns>欧几里得距离（保留4位小数，不足补乘1000）</returns>  
    public double GetDistanceData(string start, string end)
    {
        // 欧几里得距离计算公式  
        var startCoords = start.Split(',');
        var endCoords = end.Split(',');

        if (startCoords.Length != 2 || endCoords.Length != 2)
        {
            throw new ArgumentException("坐标格式不正确，应为 'y,x'");
        }

        int startx = int.Parse(startCoords[0]);
        int starty = int.Parse(startCoords[1]);
        int endx = int.Parse(endCoords[0]);
        int endy = int.Parse(endCoords[1]);

        double distance = Math.Sqrt(Math.Pow(Math.Abs(endx - startx), 2) + Math.Pow(Math.Abs(endy - starty), 2));
        double roundedDistance = Math.Round(distance, 4); // 保留4位小数  

        return roundedDistance;
    }

    /// <summary>  
    /// 查询库存  
    /// </summary>  
    /// <param name="sfpInventoryDto">SFP库存数据传输对象</param>  
    /// <returns>先进先出的库存列表</returns>  
    private async Task<List<InventoryManagementEntity>> GetFIFOInventoryAsync(TemporarilyWarehouse sfpInventoryDto)
    {
        // 检查参数有效性  
        if (sfpInventoryDto == null) throw new ArgumentNullException(nameof(sfpInventoryDto));
        //if (string.IsNullOrEmpty(sfpInventoryDto.Materialcode)) throw new ArgumentException("编码不能为空", nameof(sfpInventoryDto.Materialcode));

        // 查询本地库存数据  


        var inventoryList = await _inventoryService.GetInventoryList(new InventoryManagementDto
        {
            //物料和批次号查询  
            MaterialCode = sfpInventoryDto.Materialcode,
            Batch = sfpInventoryDto.Materialname,
            WarehousePositionCode = sfpInventoryDto.Hoistopening,

        });

        if (inventoryList == null || !inventoryList.Any())
        {
            return inventoryList;
            throw new InvalidOperationException($"未找到物料编码号为 {sfpInventoryDto.Materialcode} 的库存数据");
        }

        // 过滤可用库存 时间和库位有余位的  
        var filteredList = inventoryList.Where(i => i.Available == 0)
                                        .OrderBy(i => i.DateofReceipt)
                                        .ThenBy(i => i.CreateTime)
                                        .ToList();
        // 返回过滤后的列表  
        return filteredList;
    }

    /// <summary>  
    /// 插入测试数据  
    /// </summary>  
    private List<SFPInventoryDto> SeedInventoryItems()
    {
        // 插入测试数据  
        list.AddRange(new[]
        {
           new SFPInventoryDto
           {
               id = 67844,
               organize_code = "P1",
               qty = 20,
               date_code = "20231001",
               is_sync = 0,
               create_date_time = DateTime.Now.AddDays(-5),
               modify_date_time = DateTime.Now,
               note_2 = "测试备注2",
               note_3 = "测试备注3"
           },
           new SFPInventoryDto
           {
               id = 67845,
               organize_code = "P2",
               qty = 15,
               date_code = "20231002",
               is_sync = 1,
               create_date_time = DateTime.Now.AddDays(-10),
               modify_date_time = DateTime.Now,
               note_2 = "测试备注2-2",
               note_3 = "测试备注3-2"
           },
           new SFPInventoryDto
           {
               id = 67846,
               organize_code = "P3",
               qty = 10,
               date_code = "20231003",
               is_sync = 0,
               create_date_time = DateTime.Now.AddDays(-15),
               modify_date_time = DateTime.Now,
               note_2 = "测试备注2-3",
               note_3 = "测试备注3-3"
           }
       });
        return list;
        // 假设 _dbContext 是数据库上下文  
        //_dbContext.InventoryItems.AddRange(list);  
        //await _dbContext.SaveChangesAsync();  
    }

    /// <summary>
    /// 暂存出库导入
    /// </summary>
    /// <param name="file"></param>
    /// <returns></returns>
    [UnitOfWork]
    [DisplayName("导入")]
    [ApiDescriptionSettings(Name = "TemporaryWcsimport"), HttpPost]
    public virtual async Task<List<TemporaryDto>> TemporaryWcsimport([Required] IFormFile file)
    {

        if (file != null)
        {
            var fileDir = AppDomain.CurrentDomain.BaseDirectory + @"ImportFile";
            if (!Directory.Exists(fileDir))

            {
                Directory.CreateDirectory(fileDir);
            }
            //文件名称
            string fileName = file.FileName;
            //文件类型
            string fileType = file.FileName.Substring(fileName.LastIndexOf("."));
            //上传的文件的路径
            string filePath = fileDir + $@"/{fileName}";
            if (File.Exists(filePath))
            {
                File.Delete(filePath);
            }
            using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
            {
                file.CopyTo(fs);
            }

            if (fileType == ".xlsx" || fileType == ".xls")
            {
                ExcelHelper eh = new ExcelHelper(filePath);
                DataTable dt = eh.ExcelToDataTable("", true);
                eh.Dispose();
                if (dt == null || dt.Rows.Count == 0)
                {
                    throw Oops.Oh("文件为空！");
                }

                #region "校验模板字段"
                if (!dt.Columns.Contains("序号") || !dt.Columns.Contains("出口编码") || !dt.Columns.Contains("托盘码"))
                {
                    throw Oops.Oh("文件模板错误！请重新下载模板");
                }
                var orderid = string.Empty;
                //if (dt.Columns.Contains("任务号"))
                //{

                //    //不存在订单编号自己生成
                //    orderid = await _orderInservice.GenerateOrderIdAsync();
                //}
                orderid = await _orderInservice.GenerateOrderIdAsync();
                #endregion

                List<TemporaryDto> listLoad = new List<TemporaryDto>();
                int i = 0;
                foreach (DataRow row in dt.Rows)
                {
                    i++;
                    //校验关联信息是否正确仓库代码
                    //if (!string.IsNullOrEmpty(row["任务号"].GetCString()))
                    //{
                    //    orderid = row["任务号"].GetCString();
                    //}
                    orderid = await _orderInservice.GenerateOrderIdAsync();

                    TemporaryDto orderInfoEntity = new TemporaryDto
                    {
                        TaskNumber = orderid,
                        SortId = row["序号"].GetCInt(),
                        HoistOpening = row["出口编码"].GetCString(),
                        TrayCode = row["托盘码"].GetCString(),
                        Status = TaskStatusEnmu.NoStarte,
                        Type = TaskTypeEnum.Outbound
                    };
                
                    //用唯一索引查询非重复 
                    Expression<Func<WCSTaskManagementEntity, bool>> whereExpression = whereExp =>
                        whereExp.TaskNumber.Equals(orderInfoEntity.TaskNumber);
                    var data = await _wcsTaskEntity.GetFirstAsync(whereExpression);
                    if (data == null)
                    {
                        #region "自定义校验"

                        orderInfoEntity.CreateUserId = _userinfo.UserId;
                        orderInfoEntity.CreateTime = DateTime.Now;
                        orderInfoEntity.CreateOrgId = _userinfo.OrgId;
                        orderInfoEntity.CreateUserId = _userinfo.UserId;
                        orderInfoEntity.CreateUserName = _userinfo.RealName;

                        data = _wcsTaskEntity.Adapt(orderInfoEntity);
                        #endregion

                        var rtAdd = await _wcsTaskEntity.AsInsertable(data).ExecuteCommandAsync();
                        if (rtAdd != 1)
                        {
                            orderInfoEntity.ResultCode = 10004;
                            orderInfoEntity.Message = "插入失败";
                        }
                    }
                    else
                    {
                        #region "自定义校验"  

                        data.UpdateUserId = _userinfo.UserId;
                        data.UpdateTime = DateTime.Now;
                        data.UpdateUserName = _userinfo.RealName;
                        data.TrayCode = orderInfoEntity.TrayCode;
                        data.MaterialCode = orderInfoEntity.MaterialCode;
                        data.HoistOpening = orderInfoEntity.HoistOpening;
                        // 使用 Mapster 的 Adapt 方法将 orderInfoEntity 的值替换到 data 中  
                        #endregion
                        //不能用Adapt替换因为importPosition中没主键

                        var order = data.Adapt(orderInfoEntity);
                        var rtUpdate = await _wcsTaskEntity.AsUpdateable(order).ExecuteCommandAsync();
                        if (rtUpdate != 1)
                        {
                            orderInfoEntity.ResultCode = 10004;
                            orderInfoEntity.Message = "更新失败";
                        }
                    }

                    if (orderInfoEntity.ResultCode != 200)
                        listLoad.Add(orderInfoEntity);
                    SerilogHelper.WriteLog("导入数据" + JsonConvert.SerializeObject(orderInfoEntity));

                }

                // 返回导入失败数据
                if (listLoad.Count > 0)
                {
                    return listLoad;
                }
            }
            else
            {
                throw Oops.Oh(10002, "文件内容为空！");
            }
        }
        else
        {
            throw Oops.Oh(10002, "请选择文件！");
        }
        return null;
    }
    
}